#include "MaterialListView.h"
#include "Basic/FileSystem.h"
#include "../Resource.h"
#include "Editor.h"
namespace FinalRendering
{
	namespace Editor
	{
		using namespace GxRendering::UI;

		MaterialListView::MaterialListView()
		{
			BackColor = Color(255,255,255,255);
			rows = cols = 1;
			SelectedIndex = -1;
			SetThumbnailSize(128);
			sb = new ScrollBar();
			sb->SetOrientation(SO_VERTICAL);
			sb->SetWidth(16);
			sb->SmallChange = 50;
			sb->LargeChange = 500;
			sb->Parent = this;
			lbl = new Label();
			lbl->AutoSize = false;
			matPtr = 0;
			loaderLock.Lock();
			GxWinSystem::Threading::gxThreadProc * proc = new GxWinSystem::Threading::gxThreadProc();
			proc->Bind(this, &MaterialListView::LoadMaterials);
			loaderThread = new GxWinSystem::Threading::gxThread(proc);
			loaderThread->SetPriority(GxWinSystem::Threading::gxThread::gxtpLowest);
			loaderThread->Start();

			BorderStyle = BS_LOWERED;

			imgFolder = new Image(&Bitmap(IDB_FOLDER),&Color(0,0,0,0));

			OnDblClick.Bind(this, &MaterialListView::DoubleClicked);

			materials.SetSize(CacheSize);
			for (int i = 0; i<CacheSize; i++)
			{
				materials[i].material = new EditorMaterial();
				materials[i].itemId = -1;
			}
		}

		MaterialListView::~MaterialListView()
		{
			for (int i = 0; i<CacheSize; i++)
			{
				delete materials[i].material;
			}
		}

		void MaterialListView::HandleMessage(const GxRendering::UI::UI_MsgArgs * Args)
		{
			if (Args->Type == MSG_UI_MOUSEWHEEL)
				MouseWheel(this, *((UIMouseEventArgs *)Args->Data));
		}

		void MaterialListView::MouseWheel(GxRendering::UI::UI_Base * sender, GxRendering::UI::UIMouseEventArgs & e)
		{
			int pos = sb->GetPosition();
			if (e.Delta < 0)
			{
				pos += 80;
				if (pos > sb->GetMax())
					pos = sb->GetMax();
				sb->SetPosition(pos);
			}
			else
			{
				pos -= 80;
				if (pos < 0)
					pos = 0;
				sb->SetPosition(pos);
			}
		}

		void MaterialListView::LoadMaterials()
		{
			while (1)
			{
				loaderLock.Lock();
				int beginPos = sb->GetPosition()/itemHeight * cols;
				int endPos = beginPos + (rows+2) * cols;
				if (endPos > items.Count())
					endPos = items.Count();
				bool loaded = false;
				for (int i=beginPos; i<endPos; i++)
				{
					if (items[i]->Type == Item::itFile && (!items[i]->MaterialCache || items[i]->MaterialCache->itemId != i))
					{
						matPtr ++;
						matPtr %= CacheSize;
						MaterialCacheItem * cache = materials.Buffer() + matPtr;
						
						items[i]->MaterialCache = cache;
						cache->lock.Lock();
						cache->material->Load(ExtractFilePath(items[i]->FullFileName), 
							ExtractFileName(items[i]->FullFileName), false);
						cache->itemId = i;
						cache->Uploaded = false;
						cache->lock.Unlock();
						loaded = true;
					}
				}
				loaderLock.Unlock();
				if (loaded)
				{
					SendMessage(GetEntry()->GetMainFormHandle(), WM_PAINT, NULL, NULL);
				}
				Sleep(500);
			}
		}

		void MaterialListView::StartLoading()
		{
			loaderLock.Unlock();
		}

		void MaterialListView::StopLoading()
		{
			loaderLock.Lock();
		}

		void MaterialListView::AddItem(const String & fileName, MaterialListView::Item::ItemType type)
		{
			RefPtr<Item> item = new Item();
			item->Type = type;
			item->FileName = ExtractFileName(fileName);
			item->FullFileName = fileName;
			item->Id = items.Count();
			item->MaterialCache = 0;
			items.Add(item);
		}

		void MaterialListView::SetDirectory(const String & path)
		{
			wchar_t pathBuf[1024];
			GetFullPathName(path.GetStrPtr(), 1024, pathBuf, 0);
			curPath = pathBuf;

			MakeDirectory(curPath);
			sb->SetPosition(0);
			SelectedIndex = -1;

			items.Clear();
			WIN32_FIND_DATA find;

			// Find Directories

			String patha = curPath + L"*";
			HANDLE handle = FindFirstFileW(patha.GetStrPtr(), &find);
			if (handle != INVALID_HANDLE_VALUE)
			{
				if (find.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY && find.cFileName[0]!=L'.')
					AddItem(curPath + find.cFileName, Item::itFolder);
				while (FindNextFile(handle, &find))
				{
					if (find.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY && find.cFileName[0]!=L'.')
						AddItem(curPath + find.cFileName, Item::itFolder);
				}
			}
			FindClose(handle);

			// Find Files

			patha = curPath + L"*.gmt";
			handle = FindFirstFileW(patha.GetStrPtr(), &find);
			if (handle != INVALID_HANDLE_VALUE)
			{
				AddItem(curPath + find.cFileName, Item::itFile);
				while (FindNextFile(handle, &find))
				{
					AddItem(curPath + find.cFileName, Item::itFile);
				}
			}
			FindClose(handle);

			OnDirectoryChanged.Invoke(this);
		}

		void MaterialListView::DoubleClicked(UI_Base * sender)
		{
			if (SelectedIndex != -1)
			{
				Item & selItem = *(items[SelectedIndex]);
				if (selItem.Type == Item::itFolder)
				{
					SetDirectory(curPath + selItem.FileName);
				}
			}
		}

		int MaterialListView::HitTest(int x, int y)
		{
			if (x/itemWidth >= cols)
				return -1;
			int rs = (y+sb->GetPosition())/itemHeight * cols + x/itemWidth;
			if (rs < 0)
				return -1;
			if (rs > items.Count())
				return -1;
			else
				return rs;
		}

		String MaterialListView::GetSelectedFileName()
		{
			if (SelectedIndex != -1)
				return items[SelectedIndex]->FileName;
			else
				return L"";
		}

		void MaterialListView::DoMouseDown(int X, int Y, SHIFTSTATE Shift)
		{
			Control::DoMouseDown(X,Y,Shift);
			if (!Enabled || !Visible)
				return;
			X-=Left;
			Y-=Top;
			sb->DoMouseDown(X, Y, Shift);
			if (DownInRect && X<cwidth)
			{
				SelectedIndex = HitTest(X,Y);
				if (SelectedIndex > items.Count())
					SelectedIndex = -1;
			}
		}

		void MaterialListView::DoMouseUp(int X, int Y, SHIFTSTATE Shift)
		{
			Control::DoMouseDown(X,Y,Shift);
			if (!Enabled || !Visible)
				return;
			X-=Left;
			Y-=Top;
			sb->DoMouseUp(X,Y,Shift);
		}

		void MaterialListView::DoMouseMove(int X, int Y)
		{
			if (!Enabled || !Visible)
				return;
			sb->DoMouseMove(X-Left, Y-Top);
		}

		void MaterialListView::DoKeyDown(unsigned short Key, SHIFTSTATE Shift)
		{
			Control::DoKeyDown(Key,Shift);
		}

		String MaterialListView::GetCurrentDirectory()
		{
			return curPath;
		}

		void MaterialListView::Draw(int absX, int absY)
		{
			Control::Draw(absX, absY);
			absX += Left;
			absY += Top;
			if (!Visible)
				return;
			sb->Draw(absX, absY);
			Global::ClipRects->AddRect(Rect(absX+2, absY+2, absX+cwidth-2, absY+cheight-5));
			glPushMatrix();
			glTranslatef((float)(absX+1), (float)(absY+1), 0.0f);
			DrawContents();
			glPopMatrix();
			Global::ClipRects->PopRect();

		}

		void MaterialListView::DrawContents()
		{
			for (int i=0; i<items.Count(); i++)
			{
				int posy = (i/cols)*itemHeight - sb->GetPosition();
				int posx = (i%cols)*itemWidth;
				if ( posy>-itemHeight && posy<cheight )
				{
					DrawItem(*(items[i]), posx, posy,AbsolutePosX+1+posx, AbsolutePosY+1+posy, SelectedIndex == i);
				}
			}
		}

		void MaterialListView::DrawItem(Item & item, int x, int y, int absX, int absY, bool selected)
		{
			if (selected)
			{
				glDisable(GL_TEXTURE_2D);
				
				glBegin(GL_QUADS);
					glColor4ub(232,243,253, 255);
					glVertex2i(x,y);
					glVertex2i(x+itemWidth, y);

					glColor4ub(123,205,236, 255);
					glVertex2i(x+itemWidth, y+itemHeight);
					glVertex2i(x, y+itemHeight);
				glEnd();
			}
			glColor4ub(255,255,255,255);
			bool mat = false;
			if (item.Type == Item::itFile)
			{
				if (item.MaterialCache && item.MaterialCache->lock.TryLock())
				{
					if (item.MaterialCache->itemId == item.Id)
					{
						if (!item.MaterialCache->Uploaded)
						{
							item.MaterialCache->material->GetTexture()->CreateAndUpload();
							item.MaterialCache->material->GetTexture()->ReleaseNativeMemory();
							item.MaterialCache->Uploaded = true;
						}
						item.MaterialCache->material->Bind();
						int ox = x + 10;
						int oy = y + 10;
						glBegin(GL_QUADS);
							glTexCoord2i(0,0);
							glVertex2i(ox, oy);
							glTexCoord2i(1,0);
							glVertex2i(ox+thumbSize, oy);
							glTexCoord2i(1,1);
							glVertex2i(ox+thumbSize, oy+thumbSize);
							glTexCoord2i(0,1);
							glVertex2i(ox, oy+thumbSize);
						glEnd();
						mat = true;
					}
					item.MaterialCache->lock.Unlock();
				}
				if (!mat)
				{
					glDisable(GL_TEXTURE_2D);
					int ox = x + 10;
					int oy = y + 10;
					glBegin(GL_QUADS);
						glVertex2i(ox, oy);
						glVertex2i(ox+thumbSize, oy);
						glVertex2i(ox+thumbSize, oy+thumbSize);
						glVertex2i(ox, oy+thumbSize);
					glEnd();
				}
			}
			else
			{
				int ox = x + 10;
				int oy = y + 10;
				imgFolder->Draw(ox, oy, thumbSize, thumbSize, Color(255,255,255,255));
			}
			lbl->SetText(item.FileName);
			lbl->SetWidth(itemWidth - 10);
			lbl->SetHeight(20);
			if (selected)
				lbl->FontColor = GxRendering::UI::Color(255,255,255,255);
			else
				lbl->FontColor = Color(0,0,0,255);
			Global::ClipRects->AddRect(Rect(absX+10, absY+thumbSize+15, thumbSize, 20));
			lbl->Draw(x+10, y+thumbSize + 15);
			Global::ClipRects->PopRect();
		}

		void MaterialListView::SizeChanged()
		{
			Control::SizeChanged();
			sb->Posit(Width-sb->GetWidth()-1, 1, sb->GetWidth(), Height-2);
			cwidth = Width - sb->GetWidth() - 3;
			cheight = Height - 2;
			cols = cwidth/itemWidth;
			rows = cheight/itemHeight;
			if (cols < 1)
				cols = 1;
			int contentRows = (int)ceil(items.Count()/(double)cols);
			if (contentRows*itemHeight > cheight)
			{
				int pos = sb->GetPosition();
				if (pos > contentRows * itemHeight-cheight)
					pos = contentRows * itemHeight-cheight;
				sb->SetValue(0, contentRows*itemHeight-cheight, pos);
			}
		}

		void MaterialListView::SetThumbnailSize(int size)
		{
			thumbSize = size;
			itemHeight = thumbSize + 30;
			itemWidth = thumbSize + 20;
		}
	}

}